<template>
    <v-stage
        v-if="showStage"
        ref="stageRef"
        :config="stageConfig"
        @touchstart="handleTouchStart"
        @touchmove="handleTouchMove"
        @touchend="handleTouchEnd"
        @touchcancel="handleTouchEnd"
    >
        <v-layer ref="imageLayerRef">
            <v-group
                role="bgImage-group"
                ref="imageGroupRef"
                :config="imageGroupConfig"
            >
                <v-image
                    v-for="({ img, x, y, width, height }, i) in essayImageList"
                    :key="i"
                    :config="{
                        image: img,
                        x,
                        y,
                        width,
                        height,
                    }"
                />
            </v-group>
        </v-layer>
        <v-layer ref="mainLayerRef" role="main-layer">
            <v-group
                role="mainGroup"
                ref="mainGroupRef"
                :config="mainGroupConfig"
            >
                <v-rect
                    role="mask"
                    :config="{
                        x: 0,
                        y: 0,
                        width: stageConfig.width,
                        height: imageGroupConfig.height,
                        fill: 'rgba(0, 0, 0, 0.2)',
                    }"
                ></v-rect>
                <!-- 佳词 -->
                <WordMark :config-list="wordList" @click="handleClickWord" />
                <!-- 优美表达 -->
                <SentenceMark
                    :config-list="sentencesList"
                    :default-number-mark-config="numberMarkConfig.sentence"
                    @click="handleClickSentence"
                />
                <!-- 段落 -->
                <ParapraphMark
                    :config-list="paragraphList"
                    :default-number-mark-config="numberMarkConfig.paragraph"
                    @click="handleClickParapraph"
                />
                <!-- 分数 -->
                <Score v-if="showScore" :score="45" :config="scoreConfig" />
            </v-group>
        </v-layer>
    </v-stage>
</template>
<script setup lang="ts">
import { ref, onMounted, toRefs, computed } from "vue";
import { Stage, StageConfig } from "konva/lib/Stage";
import { Layer } from "konva/lib/Layer";
import { Group, GroupConfig } from "konva/lib/Group";
import { KonvaEventObject } from "konva/lib/Node";
import FontFaceObserver from "fontfaceobserver";
import {
    addFieldsToMarksArr,
    getImageAdaptSize,
    loadImage,
} from "@/components/origin-js";
import Score from "./Score.vue";
import WordMark from "./WordMark.vue";
import SentenceMark from "./SentenceMark.vue";
import ParapraphMark from "./ParapraphMark.vue";

const props = defineProps<{
    data: EssayEvaluatorData[];
    config: EssayEvaluatorConfig;
}>();
const { data, config } = toRefs(props);
const { stageWidth, stageHeight } = config.value;

const emits = defineEmits<{
    wordClick: (wordInfo: { [key: string]: any }) => void;
    sentenceClick: (sentenceInfo: { [key: string]: any }) => void;
    paragraphClick: (paragraph: { [key: string]: any }) => void;
}>();
// TODO: 这里是否需要，坐标是否是已适配好的
const standardWidth = 375;
const adaptScale = stageWidth / standardWidth;

const showStage = ref(false);
const showScore = ref(false);
// dom引用
const stageRef = ref<Stage>();
const stageConfig = ref<StageConfig>({
    width: stageWidth,
    height: stageHeight,
});
const imageLayerRef = ref<Layer>();
const imageGroupRef = ref<Group>();
const imageGroupConfig = ref<GroupConfig>({});
const mainLayerRef = ref<Layer>();
const mainGroupRef = ref<Group>();
const mainGroupConfig = ref<GroupConfig>({});
// 数据存储与处理相关
const essayImageList = ref<EssayImageAttrs[]>([]);
const wordList = ref<BaseMarkInfo[]>([]);
const sentencesList = ref<BaseMarkInfo[]>([]);
const paragraphList = ref<BaseMarkInfo[]>([]);
// 触摸和缩放事件相关
const scale = ref(1);
const minScale = 1;
const maxScale = 3;
const isDragging = ref(false);
const isPinching = ref(false);
const lastTouchPos = ref({ x: 0, y: 0 });
const lastTouchDistance = ref(0);
const lastTouchCenter = ref<{ x: number; y: number }>({ x: 0, y: 0 });

const isVertical = computed(() => config.value.dir === "vertical");
// TODO: 所有图形参数都要配置化，并暴露方法给用户修改图形参数
const numberMarkConfig = ref({
    sentence: {
        circle: {
            x: 0,
            y: 0,
            radius: 10,
            fill: "#1AFA29",
        },
        text: {
            x: -5,
            y: -6,
            text: "1",
            fontSize: 16,
            fill: "#000",
            align: "center",
        },
    },
    paragraph: {
        circle: {
            x: 0,
            y: 0,
            radius: 10,
            fill: "#1296DB",
        },
        text: {
            x: -5,
            y: -6,
            text: "1",
            fontSize: 16,
            fill: "#fff",
            align: "center",
        },
    },
});
const scoreConfig = ref({
    group: {
        x: 300 * adaptScale,
        y: 0 * adaptScale,
    },
    text: {
        fontSize: 50 * adaptScale,
        fontFamily: "Muyao",
        fill: "#D81E06",
        listening: false,
    },
    lineGroup: { x: 0, y: 0 },
    lines: [
        {
            stroke: "#D81E06",
            strokeWidth: 2,
        },
        { stroke: "#D81E06", strokeWidth: 2 },
    ],
});

onMounted(async () => {
    await handleEssayImages(); // 处理图片信息
    await setGroupSize();
    handleEssayData();
    // 判断字体是否已加载，未加载则加载字体，加载失败则不显示分数
    showScore.value = await loadFont();
    showStage.value = true;
});

// 处理图片信息
const handleEssayImages = async () => {
    const urls = data.value.map((item) => item.url);
    const imgs = [];

    for (let i = 0; i < urls.length; i++) {
        const url = urls[i];
        const img = await loadImage(url);
        // 计算图片适应屏幕的尺寸，并设置stage尺寸
        // TODO: 如果支持横向滚动，尺寸适配规则需要调整
        const { width, height } = await getImageAdaptSize(img);
        const x = isVertical ? 0 : i * width;
        const y: number = isVertical ? (i === 0 ? 0 : imgs[i - 1].height) : 0;

        imgs.push({
            img,
            x,
            y,
            width,
            height,
        });
    }

    essayImageList.value = imgs;
};
// 设置图片组和标注组尺寸
const setGroupSize = async () => {
    const size = calculateGroupSize();
    if (isVertical) {
        imageGroupConfig.value.width = mainGroupConfig.value.width = stageWidth;
        imageGroupConfig.value.height = mainGroupConfig.value.height = size;
    } else {
        imageGroupConfig.value.width = mainGroupConfig.value.width = size;
        imageGroupConfig.value.height = mainGroupConfig.value.height =
            stageHeight;
    }
};
// 计算图片组尺寸
const calculateGroupSize = () => {
    let size = 0;
    for (let i = 0; i < essayImageList.value.length; i++) {
        const { width, height } = essayImageList.value[i];
        size += isVertical ? height : width;
    }
    return size;
};

const loadFont = async () => {
    const font = new FontFaceObserver("Muyao");
    try {
        await font.load("0123456789");
        return true;
    } catch (err) {
        return false;
    }
};

// 处理 props 数据
const handleEssayData = () => {
    let wordArr: BaseMarkInfo[] = [],
        sentencesArr: BaseMarkInfo[] = [],
        paragraphArr: BaseMarkInfo[] = [];

    for (let i = 0; i < data.value.length; i++) {
        const { selections } = data.value[i];
        const { words, sentences, paragraphs } = selections;
        // 添加额外信息到marks中
        addFieldsToMarksArr([words, sentences, paragraphs], {
            paperIndex: i, // 试卷索引
            isHighlight: false, // 是否高亮
        });
        // 处理坐标
        handleMarksCooridates(words);
        handleMarksCooridates(sentences);
        handleMarksCooridates(paragraphs);
        // 添加到列表中
        wordArr = [...wordArr, ...words];
        sentencesArr = [...sentencesArr, ...sentences];
        paragraphArr = [...paragraphArr, ...paragraphs];
    }

    wordList.value = wordArr;
    sentencesList.value = sentencesArr;
    paragraphList.value = paragraphArr;
};

// 处理坐标
const handleMarksCooridates = (marks: BaseMarkInfo[]) => {
    marks.forEach((mark: BaseMarkInfo) => {
        const { paperIndex } = mark;
        const cooridates = mark.cooridates;
        cooridates.forEach((cooridate: BaseShapeAttrs) => {
            const { x1, y1, x2, y2 } = cooridate as {
                x1: number;
                y1: number;
                x2: number;
                y2: number;
            };
            const { x: imageX, y: imageY } =
                essayImageList.value[paperIndex as number];
            cooridate.x = (isVertical ? x1 : x1 + imageX) * adaptScale;
            cooridate.y = (isVertical ? y1 + imageY : y1) * adaptScale;
            cooridate.width = (x2 - x1) * adaptScale;
            cooridate.height = (y2 - y1) * adaptScale;
        });
    });
};

const handleClickWord = (e, wordInfo: { [key: string]: any }) => {
    if (!wordInfo.isHighlight) {
        cancelMarkHighlight(wordList.value);
        wordInfo.isHighlight = true;
        emits("wordClick", wordInfo);
    }
};

const handleClickSentence = (e, sentenceInfo: { [key: string]: any }) => {
    if (!sentenceInfo.isHighlight) {
        cancelMarkHighlight(sentencesList.value);
        sentenceInfo.isHighlight = true;
        emits("sentenceClick", sentenceInfo);
    }
};

const handleClickParapraph = (e, paragraphInfo: { [key: string]: any }) => {
    if (!paragraphInfo.isHighlight) {
        cancelMarkHighlight(paragraphList.value);
        paragraphInfo.isHighlight = true;
        emits("paragraphClick", paragraphInfo);
    }
};
const cancelMarkHighlight = (list) => {
    list.forEach((item) => {
        item.isHighlight = false;
    });
};

const handleTouchStart = (e: KonvaEventObject<MouseEvent | TouchEvent>) => {
    e.evt.preventDefault(); // 阻止默认行为
    const touches = e.evt.touches;
    const stage = e.target.getStage();
    const pos = stage.getPointerPosition();
    if (touches.length === 1) {
        // 单指触摸 - 准备拖动
        isDragging.value = true;
        isPinching.value = false;

        // 记录触摸起始位置
        lastTouchPos.value = {
            x: touches[0].clientX,
            y: touches[0].clientY,
        };
    } else if (touches.length === 2) {
        // 双指触摸 - 准备缩放
        isPinching.value = true;
        isDragging.value = false;

        // 计算两指间的距离和中心点
        const touch1 = touches[0];
        const touch2 = touches[1];

        lastTouchCenter.value = {
            x: (touch1.clientX + touch2.clientX) / 2,
            y: (touch1.clientY + touch2.clientY) / 2,
        };

        lastTouchDistance.value = Math.sqrt(
            Math.pow(touch2.clientX - touch1.clientX, 2) +
                Math.pow(touch2.clientY - touch1.clientY, 2)
        );
    }
};
const handleTouchMove = (e: KonvaEventObject<MouseEvent | TouchEvent>) => {
    e.evt.preventDefault(); // 阻止默认行为

    const touches = e.evt.touches;
    const imageLayer = getRefRealNode(imageLayerRef);
    const imageGroup = getRefRealNode(imageGroupRef);
    const mainLayer = getRefRealNode(mainLayerRef);
    const mainGroup = getRefRealNode(mainGroupRef);

    if (touches.length === 1 && isDragging.value) {
        // 单指触摸移动 - 处理拖动
        const currentPos = {
            x: touches[0].clientX,
            y: touches[0].clientY,
        };

        // 计算移动距离
        const dx = currentPos.x - lastTouchPos.value.x;
        const dy = currentPos.y - lastTouchPos.value.y;

        const tox = imageGroup.x() + dx;
        const toy = imageGroup.y() + dy;
        // 应用移动
        imageGroup.position({
            x: tox,
            y: toy,
        });

        // 同步遮罩的移动
        mainGroup.position({
            x: tox,
            y: toy,
        });

        // 更新上一次触摸位置
        lastTouchPos.value = currentPos;

        // 应用边界限制
        limitPosition();
        imageLayer.batchDraw();
        mainLayer.batchDraw();
    } else if (touches.length === 2 && isPinching.value) {
        // 双指触摸移动 - 处理缩放
        const touch1 = touches[0];
        const touch2 = touches[1];

        // 计算新的中心点和距离
        const currentCenter = {
            x: (touch1.clientX + touch2.clientX) / 2,
            y: (touch1.clientY + touch2.clientY) / 2,
        };

        const currentDistance = Math.sqrt(
            Math.pow(touch2.clientX - touch1.clientX, 2) +
                Math.pow(touch2.clientY - touch1.clientY, 2)
        );

        if (lastTouchDistance.value > 0) {
            // 计算缩放因子
            const scaleFactor = currentDistance / lastTouchDistance.value;

            // 计算新的缩放比例
            const newScale = Math.max(
                minScale,
                Math.min(maxScale, scale.value * scaleFactor)
            );

            if (newScale !== scale.value) {
                // 计算缩放中心点在图片组上的相对位置
                const centerPointOnGroup = {
                    x: (lastTouchCenter.value.x - imageGroup.x()) / scale.value,
                    y: (lastTouchCenter.value.y - imageGroup.y()) / scale.value,
                };

                // 应用新的缩放比例
                imageGroup.scale({ x: newScale, y: newScale });
                mainGroup.scale({ x: newScale, y: newScale });

                // 调整位置，使缩放中心点保持在双指中心
                const posX = currentCenter.x - centerPointOnGroup.x * newScale;
                const posY = currentCenter.y - centerPointOnGroup.y * newScale;
                imageGroup.position({
                    x: posX,
                    y: posY,
                });
                mainGroup.position({
                    x: posX,
                    y: posY,
                });

                scale.value = newScale;
            }

            // 处理平移
            const dx = currentCenter.x - lastTouchCenter.value.x;
            const dy = currentCenter.y - lastTouchCenter.value.y;

            if (dx !== 0 || dy !== 0) {
                const tx = imageGroup.x() + dx;
                const ty = imageGroup.y() + dy;
                imageGroup.position({
                    x: tx,
                    y: ty,
                });
                // 同步遮罩的平移
                mainGroup.position({
                    x: tx,
                    y: ty,
                });
            }

            // 更新上一次的中心点和距离
            lastTouchCenter.value = currentCenter;
            lastTouchDistance.value = currentDistance;

            // 应用边界限制
            limitPosition();
            imageLayer.batchDraw();
            mainLayer.batchDraw();
        }
    }
};
const handleTouchEnd = (e: KonvaEventObject<MouseEvent | TouchEvent>) => {
    const touches = e.evt.touches;
    const imageLayer = getRefRealNode(imageLayerRef);
    const mainLayer = getRefRealNode(mainLayerRef);

    if (touches.length === 1) {
        // 从双指变为单指
        isPinching.value = false;
        isDragging.value = true;

        // 记录新的触摸位置
        lastTouchPos.value = {
            x: touches[0].clientX,
            y: touches[0].clientY,
        };

        // 应用边界限制
        limitPosition();
        imageLayer.batchDraw();
        mainLayer.batchDraw();
    } else {
        isPinching.value = false;
        isDragging.value = false;
        lastTouchCenter.value = { x: 0, y: 0 };
        lastTouchDistance.value = 0;
    }
};
const limitPosition = () => {
    const imageGroup = getRefRealNode(imageGroupRef);
    const mainGroup = getRefRealNode(mainGroupRef);
    const groupWidth = imageGroup.width() * scale.value;
    const groupHeight = imageGroup.height() * scale.value;

    let x = imageGroup.x();
    let y = imageGroup.y();

    // 水平方向限制
    if (groupWidth <= stageWidth) {
        // 如果图片组宽度小于舞台宽度，则水平居中
        x = (stageWidth - groupWidth) / 2;
    } else {
        // 否则限制在边界内
        const minX = stageWidth - groupWidth;
        const maxX = 0;
        x = Math.max(minX, Math.min(maxX, x));
    }

    // 垂直方向限制
    if (groupHeight <= stageHeight) {
        // 如果图片组高度小于舞台高度，则垂直居中
        y = (stageHeight - groupHeight) / 2;
    } else {
        // 否则限制在边界内
        const minY = stageHeight - groupHeight;
        const maxY = 0;
        y = Math.max(minY, Math.min(maxY, y));
    }

    // 应用新位置
    imageGroup.position({ x, y });
    mainGroup.position({ x, y });
};
const getRefRealNode = (v) => v.value.getNode();
</script>
<style lang="scss" scoped></style>
